/*
 *  Bug.m
 *  pogostick
 *
 *  Created by daddy (chuck@kollar.com) on 8/26/09.
 *  Copyright 2009 Charles P. Kollar All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#import "Bug.h"

@implementation Bug

@synthesize space;
@synthesize gravity;
@synthesize riderMass;
@synthesize group;
@synthesize handL;
@synthesize handR;
@synthesize legTLBody;
@synthesize legTRBody;
@synthesize legTLShape;
@synthesize legTRShape;
@synthesize headBody;
@synthesize trunkBody;
@synthesize headShape;
@synthesize trunkShape;
@synthesize footL;
@synthesize footR;
@synthesize neckJoint1;
@synthesize neckJoint2;
@synthesize sholderR;
@synthesize sholderL;
@synthesize hipR;
@synthesize hipL;


// I would try using a gear joint with a 1:1 ratio, a force maximum, and then animate the phase parameter of
// the joint. This will look much less springy and wavy. Beyond that, actually animating him... I have no idea,
// you are blazing new ground I think.

-(id)initInSpace: (cpSpace *)sp group: (unsigned int)gr position: (cpVect)p size: (cpVect)size;
{
    if (self = [super init])
    {
		[self setSpace:sp];
		[self setGravity:SPACE_GRAVITY];
		[self setRiderMass:50.0f];
		[self setGroup:gr];
		
		cpFloat X = p.x, Y=p.y;
		
		cpFloat trunkRadius = size.x*.75f;
		trunkBody = cpBodyNew(4.0, cpMomentForCircle(2.0, 0.0, trunkRadius, cpvzero));
		trunkBody->p = cpv(X,Y);
		cpSpaceAddBody(space, trunkBody);
		trunkShape = cpCircleShapeNew(trunkBody, trunkRadius, cpvzero);
		trunkShape->e = 30.0; trunkShape->u = 2.5; // so that the bug bounces...
		trunkShape->group = group;
        trunkShape->collision_type = kColl_Bug;
		cpSpaceAddShape(space, trunkShape);
		
		cpFloat headRadius = size.x*.25f;
		headBody = cpBodyNew(0.1, cpMomentForCircle(2.0, 0.0, headRadius, cpvzero));
		headBody->p = cpvadd(trunkBody->p, cpv(0.0f, trunkRadius+headRadius));
		cpSpaceAddBody(space, headBody);
		headShape = cpCircleShapeNew(headBody, headRadius, cpvzero);
		headShape->e = .3; headShape->u = 2.5;
		headShape->group = group;
        headShape->collision_type = kColl_Bug;
		cpSpaceAddShape(space, headShape);
		
		// Add antenna to the head with a joint for effect....
		cpFloat antennaMultiple = 2.5f;
		antennaRShape = cpSegmentShapeNew(headBody, cpv(headRadius*cos(M_PI/4), headRadius*sin(M_PI/4)), cpv(antennaMultiple*headRadius*cos(M_PI/4), antennaMultiple*headRadius*sin(M_PI/4)), 0.0f);
		antennaRShape->e = 1.0; antennaRShape->u = 1.0;
		antennaRShape->group = group;
		cpSpaceAddShape(space, antennaRShape);
		antennaLShape = cpSegmentShapeNew(headBody, cpv(headRadius*cos(M_PI*3/4), headRadius*sin(M_PI*3/4)), cpv(antennaMultiple*headRadius*cos(M_PI*3/4), antennaMultiple*headRadius*sin(M_PI*3/4)), 0.0f);
		antennaLShape->e = 1.0; antennaLShape->u = 1.0;
		antennaLShape->group = group;
		cpSpaceAddShape(space, antennaLShape);
#if 0
		// Add eyes to the head with a joint and stalk for effect....
		headShape = cpCircleShapeNew(headBody, headRadius/4, cpv(0.5*headRadius*cos(M_PI/4), 0.5*headRadius*sin(M_PI/4)));
		headShape->e = .3; shape->u = 2.5;
		headShape->group = group;
		cpSpaceAddShape(space, headShape);
		shape = cpCircleShapeNew(headBody, headRadius/4, cpv(0.5*headRadius*cos(M_PI*3/4), 0.5*headRadius*sin(M_PI*3/4)));
		shape->e = .3; shape->u = 2.5;
		shape->group = group;
		cpSpaceAddShape(space, shape);	
#endif
		// legs....
		legTRBody = cpBodyNew(0.2, 0.5);
		cpFloat armAttach = 0.5;
		cpFloat armAnglex = M_PI*0.4;
		cpFloat armLen = 1.4;
        
		legTRBody->p = cpvadd(trunkBody->p, cpv(armAttach*trunkRadius*cos(armAnglex), armAttach*trunkRadius*sin(armAnglex)));
		cpSpaceAddBody(space, legTRBody);
		handR = cpv(armLen*trunkRadius*cos(armAnglex), armLen*trunkRadius*sin(armAnglex));
		legTRShape = cpSegmentShapeNew(legTRBody, cpv(armAttach*trunkRadius*cos(armAnglex), armAttach*trunkRadius*sin(armAnglex)), handR, 0.0f);
		legTRShape->e = 0.0; legTRShape->u = 1.0;
		legTRShape->group = group;// Don't collide objects in the same non-zero group
		cpSpaceAddShape(space, legTRShape);
        
        sholderR = cpPinJointNew(trunkBody, legTRBody, cpv(armAttach*trunkRadius*cos(armAnglex), armAttach*trunkRadius*sin(armAnglex)), cpvzero);
		cpSpaceAddConstraint(space, sholderR);

		legTLBody = cpBodyNew(0.2, 0.5);
		legTLBody->p = cpvadd(trunkBody->p, cpv(-armAttach*trunkRadius*cos(armAnglex), armAttach*trunkRadius*sin(armAnglex)));
		cpSpaceAddBody(space, legTLBody);
		handL = cpv(-armLen*trunkRadius*cos(armAnglex), armLen*trunkRadius*sin(armAnglex));
		legTLShape = cpSegmentShapeNew(legTLBody, cpv(-armAttach*trunkRadius*cos(armAnglex), armAttach*trunkRadius*sin(armAnglex)), handL, 0.0f);
		legTLShape->e = 0.0; legTLShape->u = 1.0;
		legTLShape->group = group;// Don't collide objects in the same non-zero group
		cpSpaceAddShape(space, legTLShape);
        
        sholderL = cpPinJointNew(trunkBody, legTLBody, cpv(-armAttach*trunkRadius*cos(armAnglex), armAttach*trunkRadius*sin(armAnglex)), cpvzero);
		cpSpaceAddConstraint(space, sholderL);
#if 0
		legBRBody = cpBodyNew(0.1, 0.5);
		cpFloat legAnglex = -armAnglex;
		cpFloat legLen = 1.2;
		legBRBody->p = cpvadd(trunkBody->p, cpv(armAttach*trunkRadius*cos(legAnglex), armAttach*trunkRadius*sin(legAnglex)));
		cpSpaceAddBody(space, legBRBody);
		footR = cpv(legLen*trunkRadius*cos(legAnglex), legLen*trunkRadius*sin(legAnglex));
		legBRShape = cpSegmentShapeNew(legBRBody, cpv(armAttach*trunkRadius*cos(legAnglex), armAttach*trunkRadius*sin(legAnglex)), footR, 0.0f);
		legBRShape->e = 0.0; legBRShape->u = 1.0;
		legBRShape->group = group;// Don't collide objects in the same non-zero group
		cpSpaceAddShape(space, legBRShape);
        
        hipR = cpPinJointNew(trunkBody, legBRBody, cpv(armAttach*trunkRadius*cos(legAnglex), armAttach*trunkRadius*sin(legAnglex)), cpv(0,0));
		cpSpaceAddConstraint(space, hipR);

		legBLBody = cpBodyNew(0.1, 0.5);
		legBLBody->p = cpvadd(trunkBody->p, cpv(-armAttach*trunkRadius*cos(legAnglex), armAttach*trunkRadius*sin(legAnglex)));
		cpSpaceAddBody(space, legBLBody);
		footL = cpv(-legLen*trunkRadius*cos(legAnglex), legLen*trunkRadius*sin(legAnglex));
		legBLShape = cpSegmentShapeNew(legBLBody, cpv(-armAttach*trunkRadius*cos(legAnglex), armAttach*trunkRadius*sin(legAnglex)), footL, 0.0f);
		legBLShape->e = 0.0; legBLShape->u = 1.0;
		legBLShape->group = group;// Don't collide objects in the same non-zero group
		cpSpaceAddShape(space, legBLShape);
        hipL = cpPinJointNew(trunkBody, legBLBody, cpv(-armAttach*trunkRadius*cos(legAnglex), armAttach*trunkRadius*sin(legAnglex)), cpv(0,0));
		cpSpaceAddConstraint(space, hipL);
#endif
		
		// neck joint...
        neckJoint1 = cpPinJointNew(trunkBody, headBody, cpv(0.0f,trunkRadius), cpv(0.0f,-headRadius));
		cpSpaceAddConstraint(space, neckJoint1);
		// Gear Joint - Allows you to lock the rotation of two bodies together and set a ratio of their rotation rates.
		// Rotary servos: By using a gear joint with a 1:1 ratio, you lock the rotation of the two bodies together.
		// If you changed the rotary offset dynamically, the joint would quickly converge on the new value.
		// By setting a force maximum, you can at least stop it's movement if it gets jammed by something.
		// Now you can set the maximum convergence speed as well. If the joint is off by more than a certain amount,
		// it will correct itself using a linear speed.
		// A gear joint rotates from 0 - 2*pi radians, where 0 is down, pi is up, and pi/2 is to the right.
		neckJoint2 = cpGearJointNew(trunkBody, headBody, 0.0f, 1.0f);
		neckJoint2->maxForce = 10000.0f;
		cpSpaceAddConstraint(space, neckJoint2);
		
    }
    return self;
}

-(void) performActions
{
    
}

- (void) dealloc
{
    // The order of releasing Chipmunk Physics things is important...
    // First you remove things from the space, then you can free the shapes,
    // constraints, and bodies (in that order).
    // If you forget a shape or constraint after freeing a body, you will get
    // an illegal mem ref... and your program will crash...
    cpSpaceRemoveShape(space, trunkShape);
    cpSpaceRemoveShape(space, headShape);
    cpSpaceRemoveShape(space, antennaLShape);
    cpSpaceRemoveShape(space, antennaRShape);
    cpSpaceRemoveShape(space, legTRShape);
    cpSpaceRemoveShape(space, legTLShape);
    
    cpSpaceRemoveConstraint(space, neckJoint1);
    cpSpaceRemoveConstraint(space, neckJoint2);
    cpSpaceRemoveConstraint(space, sholderR);
    cpSpaceRemoveConstraint(space, sholderL);
    //cpSpaceRemoveConstraint(space, hipR);
    //cpSpaceRemoveConstraint(space, hipL);
    
    cpSpaceRemoveBody(space, trunkBody);
    cpSpaceRemoveBody(space, headBody);
    cpSpaceRemoveBody(space, legTRBody);
    cpSpaceRemoveBody(space, legTLBody);
  
    cpShapeFree(trunkShape); cpShapeFree(headShape);
    cpShapeFree(antennaRShape); cpShapeFree(antennaRShape);
    cpShapeFree(legTRShape); cpShapeFree(legTLShape);
 
    cpConstraintFree(neckJoint1); cpConstraintFree(neckJoint2);
    cpConstraintFree(sholderR); cpConstraintFree(sholderL);
    //cpConstraintFree(hipR); cpConstraintFree(hipL);
    
    cpBodyFree(trunkBody); cpBodyFree(headBody);
	cpBodyFree(legTRBody); cpBodyFree(legTLBody);
    //cpBodyFree(legBRBody); cpBodyFree(legBLBody);

	[super dealloc];
}

@end
